#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <openssl/ssl.h>
#include <openssl/err.h>

#define PROXY_PORT 8888
#define DEST_HOST "admin.hdedu.fun"
#define DEST_PORT 443

void handle_error(const char *msg) {
    perror(msg);
    exit(EXIT_FAILURE);
}

void init_openssl() {

    SSL_library_init();
    OpenSSL_add_all_algorithms();
    SSL_load_error_strings();


    // SSL_load_error_strings();
    // OpenSSL_add_ssl_algorithms();
}

SSL_CTX *create_context() {
    const SSL_METHOD *method;
    SSL_CTX *ctx;

    method = SSLv23_client_method(); // You might want to use TLS_client_method() for modern versions

    if ((ctx = SSL_CTX_new(method)) == NULL) {
        handle_error("Error creating SSL context");
        ERR_print_errors_fp(stderr);
        printf("create_context failed 111 \n");
    }

    printf("create_context success 222 \n");

    return ctx;
}

int start_ssl() {
    init_openssl();

    int server_fd, client_fd;
    struct sockaddr_in server_addr, client_addr;
    socklen_t client_len = sizeof(client_addr);
    // SSL_CTX *ctx;
    // SSL *ssl;

    // ctx = create_context();
    SSL_CTX *ctx = create_context();
    // SSL_CTX *ctx = SSL_CTX_new(SSLv23_client_method());

    // Create socket
    if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
        handle_error("Error creating socket");
    }

    // Set up server address struct
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    server_addr.sin_port = htons(PROXY_PORT);

    // Bind socket
    if (bind(server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {
        handle_error("Error binding");
    }

    // Listen for incoming connections
    if (listen(server_fd, 10) == -1) {
        handle_error("Error listening");
    }

    printf("Proxy listening on port %d...\n", PROXY_PORT);

    while (1) {
        // Accept a connection
        if ((client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &client_len)) == -1) {
            handle_error("Error accepting connection");
        }

        // Create SSL object
        // ssl = SSL_new(ctx);
        SSL *ssl = SSL_new(ctx);
        SSL_set_fd(ssl, client_fd);

        // Perform SSL handshake
        printf("Before SSL_connect ...\n");
        if (SSL_accept(ssl) <= 0) {
        // if (SSL_connect(ssl) <= 0) {
            ERR_print_errors_fp(stderr);
            close(client_fd);
            continue;
        }
        printf("After SSL_connect ...\n");

        // At this point, you can read and write to `ssl` as if it's a regular socket

        // Example: Send HTTPS request to destination server
        const char *request = "GET / HTTP/1.1\r\nHost: " DEST_HOST "\r\nConnection: close\r\n\r\n";
        SSL_write(ssl, request, strlen(request));

        // Example: Receive and print the response
        char buffer[4096];
        int bytes_received = SSL_read(ssl, buffer, sizeof(buffer) - 1);
        if (bytes_received > 0) {
            buffer[bytes_received] = '\0';
            printf("Server Response:\n%s\n", buffer);
        }

        // Clean up
        SSL_shutdown(ssl);
        close(client_fd);
    }

    // Clean up OpenSSL
    SSL_CTX_free(ctx);
    EVP_cleanup();
    ERR_free_strings();

    return 0;
}

// void print_supported_versions(const SSL_METHOD *method) {
//     printf("Supported Versions for %s:\n", SSL_TXT(SSL_METHOD_get_version(method)));

//     for (int version = 0; ; version++) {
//         const char *version_name = SSL_get_version(method, version);
//         if (!version_name) {
//             break;
//         }

//         printf("- %s\n", version_name);
//     }

//     printf("\n");
// }